home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 17 / CU Amiga Magazine's Super CD-ROM 17 (1997)(EMAP Images)(GB)[!][issue 1997-12].iso / CUCD / Programming / DiceSource / lib / unix / vfork.c < prev   
C/C++ Source or Header  |  1997-09-09  |  10KB  |  497 lines

  1.  
  2. /*
  3.  *  UNIX/VFORK.C
  4.  *
  5.  *    (c)Copyright 1992-1997 Obvious Implementations Corp.  Redistribution and
  6.  *    use is allowed under the terms of the DICE-LICENSE FILE,
  7.  *    DICE-LICENSE.TXT.
  8.  */
  9.  
  10. #include <exec/types.h>
  11. #include <exec/nodes.h>
  12. #include <exec/lists.h>
  13. #include <exec/ports.h>
  14. #include <exec/tasks.h>
  15. #include <dos/dos.h>
  16. #include <dos/dosextens.h>
  17. #include <dos/dostags.h>
  18. #include <stdio.h>
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #include <fcntl.h>
  22. #include <ioctl.h>
  23. #include <time.h>
  24. #include <errno.h>
  25. #include <setjmp.h>
  26. #include <sys/wait.h>
  27. #include <sys/resource.h>
  28. #include <lib/misc.h>
  29. #include <lib/unix.h>
  30.  
  31. #include <clib/dos_protos.h>
  32. #include <clib/exec_protos.h>
  33. #include <clib/alib_protos.h>
  34. #include <lists.h>
  35.  
  36. #ifndef UnixToAmigaPath
  37. #define UnixToAmigaPath(path)    path
  38. #endif
  39.  
  40. typedef struct CommandLineInterface CLI;
  41. typedef struct FileLock FileLock;
  42. typedef struct MsgPort    MsgPort;
  43. typedef struct Node    Node;
  44. typedef struct List    List;
  45. typedef struct Process    Process;
  46. typedef struct Task    Task;
  47. typedef struct Segment    Segment;
  48.  
  49. typedef struct LockList {
  50.     BPTR    NextPath;
  51.     BPTR    PathLock;
  52. } LockList;
  53.  
  54. typedef struct ProcNode {
  55.     Node    pn_Node;
  56.     long    pn_Pid;
  57.     long    pn_ExitCode;
  58.     long    pn_Status;        /*    0 == running, 1 == exited   */
  59.     Task    *pn_Task;
  60.     Task    *pn_Parent;
  61.     Segment *pn_Seg;        /*    resident segment    */
  62. } ProcNode;
  63.  
  64. #define SIGBREAKF_ALL    (SIGBREAKF_CTRL_C|SIGBREAKF_CTRL_D|SIGBREAKF_CTRL_E|SIGBREAKF_CTRL_F)
  65.  
  66. extern int __InUnixFork;
  67.  
  68. __near jmp_buf _amiga_vfork_jmp;
  69. static int  vfork_fda[3];   /*    stdin , stdout, stderr    */
  70. static int  vfork_fdc[3];   /*    closable        */
  71. static int  vfork_init;
  72. static int  vfork_pid;        /*    pid's for subprocs      */
  73. static List ProcList;
  74.  
  75. typedef void *void_ptr;
  76.  
  77. void _amiga_vfork_returnc(long, ProcNode *);
  78. void amiga_vfork_exit(void);
  79.  
  80. extern void _amiga_vfork_return(void);
  81.  
  82. void
  83. amiga_vfork_exit()
  84. {
  85.     union wait status;
  86.  
  87.     amiga_vfork_sigall(SIGBREAKF_ALL);
  88.  
  89.     while (wait(&status) != -1)
  90.     ;
  91. }
  92.  
  93. /*
  94.  *  This routine is called (out of context) when a child process exits.
  95.  *  It must set the status & exit code then signal the parent
  96.  */
  97.  
  98. void
  99. _amiga_vfork_returnc(rc, pn)
  100. long rc;
  101. ProcNode *pn;
  102. {
  103.     ProcNode *scan;
  104.     Process *proc = (Process *)pn->pn_Task;
  105.  
  106.     /*
  107.      *    close stdin, stdout, stderr here to synchronize with parent
  108.      *    ?? stderr flag ??
  109.      */
  110.  
  111.     if (proc->pr_Flags & PRF_CLOSEINPUT) {
  112.     Close(proc->pr_CIS);
  113.     proc->pr_CIS = NULL;
  114.     proc->pr_Flags &= ~PRF_CLOSEINPUT;
  115.     }
  116.     if (proc->pr_Flags & PRF_CLOSEOUTPUT) {
  117.     Close(proc->pr_COS);
  118.     proc->pr_COS = NULL;
  119.     proc->pr_Flags &= ~PRF_CLOSEOUTPUT;
  120.     }
  121.  
  122.     /*
  123.      *    find job node and signal/set status
  124.      */
  125.  
  126.     Forbid();
  127.     for (scan = GetHead(&ProcList); scan; scan = GetSucc(&scan->pn_Node)) {
  128.     /*
  129.     fhprintf(((Process *)scan->pn_Parent)->pr_COS, "SCAN: %08lx: %08lx %08lx\n", scan, pn, rc);
  130.     */
  131.     if (pn == scan)
  132.         break;
  133.     }
  134.     if (scan) {
  135.     {
  136.         Segment *seg;
  137.         if (seg = pn->pn_Seg) {
  138.         if (seg->seg_UC > 0)
  139.             --seg->seg_UC;
  140.         }
  141.     }
  142.     scan->pn_ExitCode = rc;
  143.     scan->pn_Status = 1;
  144.     Signal(scan->pn_Parent, SIGF_SINGLE);
  145.     }
  146.     /*
  147.     fhprintf(((Process *)scan->pn_Parent)->pr_COS, "EXIT: %s %08lx %08lx %d\n", FindTask(NULL)->tc_Node.ln_Name, pn, scan, ((Process *)FindTask(NULL))->pr_Result2);
  148.     */
  149.     /* Permit();    leave in Forbid state */
  150. }
  151.  
  152. /*
  153.  *  called from assembly tag
  154.  */
  155.  
  156. int
  157. _amiga_vforkc()
  158. {
  159.     if (vfork_init == 0) {
  160.     vfork_init = 1;
  161.     NewList(&ProcList);
  162.     atexit(amiga_vfork_exit);
  163.     }
  164.     ++vfork_pid;
  165.  
  166.     __InUnixFork = 1;
  167.     vfork_fda[0] = -1;
  168.     vfork_fda[1] = -1;
  169.     vfork_fda[2] = -1;
  170.     vfork_fdc[0] = 0;
  171.     vfork_fdc[1] = 0;
  172.     vfork_fdc[2] = 0;
  173.     return(0);            /*    child    */
  174. }
  175.  
  176. int
  177. amiga_execlp(path, arg0, ...)
  178. char *path;
  179. char *arg0;
  180. {
  181.     return(amiga_execvp(path, &arg0));
  182. }
  183.  
  184. int
  185. amiga_execvp(path, args)
  186. char *path;
  187. char **args;
  188. {
  189.     {
  190.     Segment *seg;
  191.  
  192.     Forbid();
  193.     if ((seg = FindSegment(FilePart(path), NULL, 0)) || (seg = FindSegment(FilePart(path), NULL, 1))) {
  194.         if (seg->seg_UC >= 0)
  195.         ++seg->seg_UC;
  196.         Permit();
  197.         return(amiga_execseg(args, seg, seg->seg_Seg));
  198.     }
  199.     Permit();
  200.     }
  201.     {
  202.     BPTR segList;
  203.  
  204.     if (segList = LoadSeg(UnixToAmigaPath(path)))
  205.         return(amiga_execseg(args, NULL, segList));
  206.     }
  207.     if (strchr(UnixToAmigaPath(path), ':') == NULL) {
  208.     CLI *cli = (CLI *)BADDR(((Process *)FindTask(NULL))->pr_CLI);
  209.     BPTR segList = NULL;
  210.  
  211.     if (cli) {
  212.         LockList *ll;
  213.  
  214.         for (ll = (LockList *)BADDR(cli->cli_CommandDir); segList == NULL && ll; ll = (LockList *)BADDR(ll->NextPath)) {
  215.         if (ll->PathLock) {
  216.             BPTR lock = CurrentDir(ll->PathLock);
  217.             segList = LoadSeg(UnixToAmigaPath(path));
  218.             CurrentDir(lock);
  219.         }
  220.         }
  221.     }
  222.     return(amiga_execseg(args, NULL, segList));
  223.     }
  224. }
  225.  
  226. int
  227. amiga_execl(path, arg0, ...)
  228. char *path;
  229. char *arg0;
  230. {
  231.     return(amiga_execv(path, &arg0));
  232. }
  233.  
  234. int
  235. amiga_execv(path, args)
  236. char *path;
  237. char **args;
  238. {
  239.     {
  240.     Segment *seg;
  241.  
  242.     Forbid();
  243.     if ((seg = FindSegment(FilePart(path), NULL, 0)) || (seg = FindSegment(FilePart(path), NULL, 1))) {
  244.         if (seg->seg_UC >= 0)
  245.         ++seg->seg_UC;
  246.         Permit();
  247.         return(amiga_execseg(args, seg, seg->seg_Seg));
  248.     }
  249.     Permit();
  250.     }
  251.     {
  252.     BPTR segList;
  253.  
  254.     if (segList = LoadSeg(UnixToAmigaPath(path)))
  255.         return(amiga_execseg(args, NULL, segList));
  256.     }
  257.     return(amiga_execseg(args, NULL, NULL));
  258. }
  259.  
  260. int
  261. amiga_execseg(args, seg, segList)
  262. char **args;
  263. struct Segment *seg;
  264. long segList;
  265. {
  266.     int r = -1;
  267.     int freeSegList = (seg) ? 0 : 1;
  268.     ProcNode *pn;
  269.  
  270.     if (__InUnixFork == 0) {
  271.     if (freeSegList)
  272.         UnLoadSeg(segList);
  273.     errno = ENOEXEC;
  274.     return(-1);
  275.     }
  276.  
  277.     pn = malloc(sizeof(ProcNode));
  278.     if (pn == NULL) return(-1);
  279.  
  280.     clrmem(pn, sizeof(ProcNode));
  281.  
  282.     pn->pn_Pid = vfork_pid;
  283.     pn->pn_Parent = FindTask(NULL);
  284.     AddTail(&ProcList, &pn->pn_Node);
  285.  
  286.     /*
  287.      *    Startup the process
  288.      */
  289.  
  290.     if (segList) {
  291.     char *argStr;
  292.     {
  293.         short i;
  294.         short len;
  295.         for (i = 1, len = 0; args[i]; ++i)
  296.         len += strlen(args[i]) + 1;
  297.         argStr = malloc(len + 3);
  298.         if (argStr)
  299.         {
  300.                for (i = 1, len = 0; args[i]; ++i)
  301.                    len += sprintf(argStr + len, "%s ", args[i]);
  302.                if (len)
  303.                    --len;
  304.                argStr[len++] = '\n';   /*  so BCPL programs work right */
  305.                argStr[len] = 0;
  306.             }
  307.             else argStr = NULL;
  308.     }
  309.  
  310.     pn->pn_Node.ln_Name = (char *)rega4();
  311.     pn->pn_Seg = seg;
  312.  
  313.     pn->pn_Task = (Task *)CreateNewProcTags(
  314.         NP_Seglist, segList,
  315.         NP_FreeSeglist, freeSegList,
  316.         NP_Input, (vfork_fda[0] >= 0) ? __getfh(vfork_fda[0])->fd_Fh : Input(),
  317.         NP_CloseInput, vfork_fdc[0],
  318.         NP_Output, (vfork_fda[1] >= 0) ? __getfh(vfork_fda[1])->fd_Fh : Output(),
  319.         NP_CloseOutput, vfork_fdc[1],
  320.         NP_Error, (vfork_fda[2] >= 0) ? __getfh(vfork_fda[2])->fd_Fh : Output(),
  321.         NP_CloseError, vfork_fdc[2],
  322.         NP_StackSize, ((struct CommandLineInterface *)BADDR(((struct Process *)pn->pn_Parent)->pr_CLI))->cli_DefaultStack * 4,
  323.         NP_CommandName, args[0],
  324.         NP_Arguments, argStr,
  325.         NP_Cli, 1,
  326.         NP_ExitCode, _amiga_vfork_return,
  327.         NP_ExitData, pn,
  328.         TAG_END
  329.     );
  330.  
  331.     free(argStr);
  332.  
  333.     if (pn->pn_Task) {
  334.         r = pn->pn_Pid;
  335.     } else {
  336.         if (freeSegList)
  337.         UnLoadSeg(segList);
  338.         segList = NULL;
  339.     }
  340.     }
  341.  
  342.     /*
  343.      *    if success, invalidate descriptors that we passed to the child
  344.      *    (child process will close them)
  345.      */
  346.  
  347.     __InUnixFork = 0;
  348.  
  349.     if (r > 0) {
  350.     _IOFDS *d;
  351.     short i;
  352.  
  353.     for (i = 0; i < 3; ++i) {
  354.         if ((vfork_fda[i] >= 0) && (d = __getfh(vfork_fda[i]))) {
  355.         d->fd_Flags |= O_NOCLOSE;
  356.         close(vfork_fda[i]);
  357.         }
  358.     }
  359.     } else {
  360.     pn->pn_Status = 1;
  361.     pn->pn_ExitCode = -1;
  362.     r = pn->pn_Pid;
  363.     errno = ENOEXEC;
  364.     }
  365.  
  366.     longjmp(_amiga_vfork_jmp, r);
  367. }
  368.  
  369. /*
  370.  *  amiga_dup2() is used to flag descriptors for stdin/stdout in the exec
  371.  */
  372.  
  373. int
  374. amiga_dup2(fdi, fdo)
  375. int fdi;
  376. int fdo;
  377. {
  378.     _IOFDS *d;
  379.  
  380.     if (fdo >= 0 && fdo < 3) {
  381.     vfork_fda[fdo] = fdi;
  382.  
  383.     /*
  384.      *  determine whether the created process will be allowed to
  385.      *  close the handle.  Can't if it's marked NOCLOSE and can't
  386.      *  if we are passing the same descriptor a second or third
  387.      *  time.
  388.      */
  389.  
  390.     if ((d = __getfh(fdi)) && (d->fd_Flags & O_NOCLOSE) == 0)
  391.         vfork_fdc[fdo] = 1;
  392.     if (vfork_fda[(fdo+1)%3] == fdi)
  393.         vfork_fdc[fdo] = 0;
  394.     if (vfork_fda[(fdo+2)%3] == fdi)
  395.         vfork_fdc[fdo] = 0;
  396.     }
  397.     return(fdo);
  398. }
  399.  
  400. int
  401. wait(status)
  402. union wait *status;
  403. {
  404.     return(wait3(status, 0, NULL));
  405. }
  406.  
  407. int
  408. wait3(status, flags, rus)
  409. union wait *status;
  410. int flags;
  411. struct rusage *rus;
  412. {
  413.     ProcNode *pn = NULL;
  414.     int r = -1;
  415.  
  416.     clrmem(status, sizeof(union wait));
  417.  
  418.     for (;;) {
  419.     for (pn = GetHead(&ProcList); pn; pn = GetSucc(&pn->pn_Node)) {
  420.         if (pn->pn_Status) {
  421.         flags |= WNOHANG;
  422.         break;
  423.         }
  424.     }
  425.  
  426.     if ((flags & WNOHANG) || GetHead(&ProcList) == NULL)
  427.         break;
  428.  
  429.     {
  430.         long mask;
  431.  
  432.         mask = Wait(SIGF_SINGLE | SIGBREAKF_ALL);
  433.         if (mask & SIGBREAKF_ALL) {
  434.         amiga_vfork_sigall(mask & SIGBREAKF_ALL);
  435.         }
  436.     }
  437.     }
  438.  
  439.     if (pn) {
  440.     Remove(&pn->pn_Node);
  441.     status->w_retcode = pn->pn_ExitCode;
  442.     status->u.wu_flags |= WF_EXITED;
  443.     r = pn->pn_Pid;
  444.     free(pn);
  445.     } else {
  446.     errno = 0;
  447.     }
  448.     return(r);
  449. }
  450.  
  451. int
  452. sigcheckchld()
  453. {
  454.     ProcNode *pn;
  455.  
  456.     for (pn = GetHead(&ProcList); pn; pn = GetSucc(&pn->pn_Node)) {
  457.     if (pn->pn_Status)
  458.         return(1);
  459.     }
  460.     return(0);
  461. }
  462.  
  463. int
  464. kill(pid, signo)
  465. int pid;
  466. int signo;
  467. {
  468.     ProcNode *pn;
  469.     int r = -1;
  470.  
  471.     Forbid();
  472.     for (pn = GetHead(&ProcList); pn; pn = GetSucc(&pn->pn_Node)) {
  473.     if (pn->pn_Pid == pid && pn->pn_Status == 0) {
  474.         Signal(pn->pn_Task, SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D);
  475.         r = 0;
  476.     }
  477.     }
  478.     Permit();
  479.     return(r);
  480. }
  481.  
  482.  
  483. void
  484. amiga_vfork_sigall(mask)
  485. long mask;
  486. {
  487.     ProcNode *pn;
  488.  
  489.     Forbid();
  490.     for (pn = GetHead(&ProcList); pn; pn = GetSucc(&pn->pn_Node)) {
  491.     if (pn->pn_Status == 0)
  492.         Signal(pn->pn_Task, mask & SIGBREAKF_ALL);
  493.     }
  494.     Permit();
  495. }
  496.  
  497.